﻿using Microsoft.Win32;
using Space_X.Models;
using Space_X.Properties;
using Space_X.Views.Dialogs;
using spacex.camInterface;
using spacex.imaging;
using spacex.imaging.Commands;
using spacex.util.Enums;
using spacex.util.Interfaces;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;

namespace Space_X.ViewModels
{
    /// <summary>
    /// Represents a Camera View Model
    /// </summary>
    public class CameraViewModel : INotifyPropertyChanged, ICamObserver
    {
        #region Class fields
        private CameraModel cameraModel;
        private ImageProcessor imageProcessor;
        private PanCam panCam;
        private PanCamCollection _panCamCollection;
        private BitmapSource _overlay;
        private string lastFileName = "image.bmp";
        public int pixelRow { get; set; }
        public List<int> cutArray = new List<int>();
        public bool inStereo = false;
        #endregion // Class fields

        #region Exposed Model Properties
        /// <summary>
        /// Exposes the currently selected pancam image
        /// </summary>
        public PanCamImage PancamImage
        { 
            get { return cameraModel.PancamImage; }
            set 
            {
                if (cameraModel.PancamImage != value)
                {
                    cameraModel.PancamImage = value;
                    RaisePropertyChanged("PancamImage");
                }
            }  
        }

        /// <summary>
        /// Exposes the stereo view pancam image
        /// </summary>
        public PanCamImage StereoPanCamImage
        {
            get { return cameraModel.StereoPanCamImage; }
            set 
            {
                if (cameraModel.StereoPanCamImage != value)
                {
                    cameraModel.StereoPanCamImage = value;
                    RaisePropertyChanged("StereoPanCamImage");
                }

            }
        }

        /// <summary>
        /// Exposes the overlay
        /// </summary>
        public BitmapSource Overlay
        {
            get { return _overlay; }
            set 
            {
                if (_overlay != value)
                {
                    _overlay = value;
                    RaisePropertyChanged("Overlay");
                }
            }
        }

        /// <summary>
        /// Exposes the histogram
        /// </summary>
        public Image Histogram
        {
            get { return cameraModel.Histogram; }
            set
            {
                if (cameraModel.Histogram != value)
                {
                    cameraModel.Histogram = value;
                    RaisePropertyChanged("Histogram");
                }
            }
        }

        /// <summary>
        /// Exposes the temperature value
        /// </summary>
        public double Temperature
        {
            get { return cameraModel.Temperature; }
            set 
            {
                if (cameraModel.Temperature != value)
                {
                    cameraModel.Temperature = value;
                    RaisePropertyChanged("Temperature");
                }
            }
        }

        /// <summary>
        /// Exposes the image collection
        /// </summary>
        public PanCamCollection PanCamCollection
        {
            get { return _panCamCollection; }
            set { 
                _panCamCollection = value;
                RaisePropertyChanged("PanCamCollection");
            }
        }

        #endregion //Exposed Model Properties

        #region Constructor
        /// <summary>
        /// Constuctor
        /// </summary>
        public CameraViewModel()
        {
            imageProcessor = ImageProcessor.GetInstance();
            PanCamCollection = new PanCamCollection(this);
            PanCamCollection.InitFileWatcher();

            /* try to initialize a pancam controller (SpiStorm.dll must be present) */
            try
            {
                panCam = new PanCam();
            }
            catch(DllNotFoundException e)
            {
                MessageBox.Show(@"PanCam init failed, could not find dll. Copy SpiStorm.dll to C:\pancam\. Error: " + e);
            }

            /* try to intialize the cameraModel (image load access) */
            try
            {
                cameraModel = new CameraModel { PancamImage = new PanCamImage(), StereoPanCamImage = new PanCamImage()};
                RaisePropertyChanged("PancamImage");
                RaisePropertyChanged("StereoPanCamImage");
            }
            catch
            {
                MessageBox.Show(@"Can't load image from assembly spacex.util");
            }
        }
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion // INotifyPropertyChanged Members

        #region Utility Methods
        private void RaisePropertyChanged(string propertyName)
        {
            // take a copy to prevent thread issues
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Called from the camera interface when a captured image is ready (Observer pattern)
        /// </summary>
        public void Update()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Clears the image cut
        /// </summary>
        public void ClearImageCut()
        {
            Overlay = null;
        }

        #endregion // Utility methods

        #region Routed commands
        public ICommand GenerateHistogram { get { return new RelayCommand(GenerateHistogramExecute, GenerateHistogramCanExecute); } }
        public ICommand ApplyImageTransform { get { return new RelayCommand<object>(ImageTransformExecute, ImageTransformCanExecute); } }
        public ICommand LoadImage { get { return new RelayCommand(LoadImageExecute, LoadImageCanExecute); } }
        public ICommand SaveImage { get { return new RelayCommand(SaveImageExecute, SaveImageCanExecute); } }
        public ICommand DisplayImage { get { return new RelayCommand(DisplayImageExecute, DisplayImageCanExecute); } }
        public ICommand TakeImage { get { return new RelayCommand(TakeImageExecute, TakeImageCanExecute); } }
        public ICommand Connect { get { return new RelayCommand(ConnectExecute, ConnectCanExecute); } }
        public ICommand SelectionChanged { get { return new RelayCommand<object>(SelectionExecute, SelectionCanExecute); } }
        public ICommand SpawnHistOverlay { get { return new RelayCommand(OverlayExecute, OverlayCanExecute); } }
        public ICommand Housekeeping { get { return new RelayCommand(HousekeepingExecute, HousekeepingCanExecute); } }
        public ICommand CreateCut { get { return new RelayCommand(CreateCutExecute, CreateCutCanExecute); } }
        public ICommand SaveHistogram { get { return new RelayCommand(SaveHistogramExecute, SaveHistogramCanExecute); } }
        public ICommand SaveImageCut { get { return new RelayCommand(SaveImageCutExecute, SaveImageCutCanExecute); } }
        #endregion // Routed commands

        #region Command methods
        /// <summary>
        /// Checks wether an image cut an be executed, in our case this is always possible
        /// </summary>
        /// <returns>Wether the cut can bee executed</returns>
        private bool CreateCutCanExecute()
        {
            return true;
        }

        /// <summary>
        /// Generates the image cut by asking the image processor to process the cut array
        /// </summary>
        private void CreateCutExecute()
        {
            Overlay = imageProcessor.GenerateCutOverlay(cutArray, pixelRow) as BitmapSource;
            RaisePropertyChanged("Overlay");
        }

        /// <summary>
        /// Checks wether the histogram can be exported as a file (excel export)
        /// </summary>
        /// <returns></returns>
        private bool SaveHistogramCanExecute()
        {
            if (Histogram != null)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Exports the histogram as an excel readable format
        /// </summary>
        private void SaveHistogramExecute()
        {
            DateTime now = DateTime.Now;
            string fileName = now.Year + "_" + now.DayOfWeek + "_" + now.Hour + "_" + now.Minute + "_" + now.Second + "_" + "histogram.txt";

            TextWriter writer = new StreamWriter((string)Settings.Default.Path + @"rec\" + fileName);

            writer.WriteLine("Brigtness" + "\t" + "Pixels");

            int step = 0;

            foreach(int value in imageProcessor.histogramData){
                step++;
                int brightness = (int) (765.0f / 100.0f * step);
                if(brightness <= 765){
                    writer.WriteLine(brightness + "\t" + value);
                }
            }

            MessageBox.Show("Histogram has been saved in the main image folder.");

            writer.Close();
        }

        /// <summary>
        /// Checks wether the image cut can be saved
        /// </summary>
        /// <returns>Boolean wether the cut can be saved or not</returns>
        private bool SaveImageCutCanExecute()
        {
            if (Overlay != null)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Saves the image cut as as an excel readable text file
        /// </summary>
        private void SaveImageCutExecute()
        {
            DateTime now = DateTime.Now;
            string fileName = now.Year + "_" + now.DayOfWeek + "_" + now.Hour + "_" + now.Minute + "_" + now.Second + "_" + "imageCut.txt";

            TextWriter writer = new StreamWriter((string)Settings.Default.Path + @"rec\" + fileName);

            writer.WriteLine("Brigtness" + "\t" + "Pixel Location");

            int step = 0;

            foreach (int pixel in cutArray)
            {
                step++;
                writer.WriteLine(pixel + "\t" + step);
            }

            MessageBox.Show("The cut has been saved in the main image folder.");

            writer.Close();
        }

        /// <summary>
        /// Checks wether a image can be selected from the image browser (always allowed)
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private bool SelectionCanExecute(object obj)
        {
            return true;
        }

        /// <summary>
        /// Performs the selection when an image is clicked in the image browser
        /// </summary>
        /// <param name="obj">The name of the selected image as object</param>
        private void SelectionExecute(object obj)
        {
            foreach(PanCamImage image in PanCamCollection){
                if(image.Name == (obj as string)){
                    if (!inStereo)
                    {
                        PancamImage = image;
                        Overlay = null;
                        return;
                    }
                    else
                    {
                        StereoPanCamImage = image;
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Checks wether a captured image can execute
        /// </summary>
        /// <returns>Boolean weather the image can be dispalyd or not</returns>
        private bool DisplayImageCanExecute()
        {
            if(panCam.ImageCaptured){
                return true;
            }
            return false;
        }

        /// <summary>
        /// Loads and displays a captured pancam image
        /// </summary>
        private void DisplayImageExecute()
        {
            //TODO: Here, load the newly taken image into view
        }

        /// <summary>
        /// Checks wether the housekeeping can be executed, only true if a connection to the camera has been established
        /// </summary>
        /// <returns></returns>
        private bool HousekeepingCanExecute()
        {
            return panCam.ConnectionEstablished;
        }

        /// <summary>
        /// Performs housekeeping
        /// </summary>
        private void HousekeepingExecute()
        {
            MessageBox.Show("Sorry, not implemented yet");
        }

        /// <summary>
        /// Ckecks wether a conection to the PanCam can be establised
        /// </summary>
        /// <returns></returns>
        private bool ConnectCanExecute()
        {
            return true;
        }

        //Tries to connect to the PanCam
        private void ConnectExecute()
        {
            bool connected = panCam.ConnectToPanCam();
            panCam.ConnectionEstablished = connected;
        }

        /// <summary>
        /// Checks wether an image can be taken from the pancam
        /// </summary>
        /// <returns></returns>
        private bool TakeImageCanExecute()
        {
            return panCam.ConnectionEstablished;
        }

        /// <summary>
        /// Takes an image by supplying all entered parameters to to the panCam interface
        /// </summary>
        private void TakeImageExecute()
        {
            MessageBox.Show("Image acquired, loading...");
        }

        /// <summary>
        /// Checks wether an image can be loaded
        /// </summary>
        /// <returns></returns>
        private bool LoadImageCanExecute()
        {
            return true;
        }

        /// <summary>
        /// Load image specified by the user (must be bmp)
        /// </summary>
        private void LoadImageExecute()
        {
            //load image from file
            OpenFileDialog loadImageDialog = new OpenFileDialog();
            loadImageDialog.Filter = "Bitmap (.bmp)|*.bmp| All Files (*.*)|*.*";
            loadImageDialog.FilterIndex = 1;

            bool? userClickedOK = loadImageDialog.ShowDialog();

            if(userClickedOK == true){
                if (loadImageDialog.FileName.EndsWith(".bmp"))
                {
                    try
                    {
                        Uri filename = new Uri(loadImageDialog.FileName);
                        PancamImage.PanCamBitmap = new BitmapImage(filename);
                        RaisePropertyChanged("PancamImage");
                    }
                    catch
                    {
                        MessageBox.Show("Could not load file.");
                    }
                }
                else
                {
                    MessageBox.Show("File is the wrong format!");
                }
            }
        }

        /// <summary>
        /// Checks wether a certain image can be saved
        /// </summary>
        /// <returns></returns>
        private bool SaveImageCanExecute()
        {
            if (PancamImage.PanCamBitmap != null)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Saves the currently selected image
        /// </summary>
        private void SaveImageExecute()
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "Bitmap (.bmp)|*.bmp| All Files (*.*)|*.*";
            saveFileDialog.FileName = lastFileName;
            saveFileDialog.InitialDirectory = Settings.Default.Path + "img";

            WriteableBitmap imageToStore = new WriteableBitmap(PancamImage.PanCamBitmap);

            bool? userClickedOK = saveFileDialog.ShowDialog();

            if(userClickedOK == true){
                string path = saveFileDialog.FileName;
                lastFileName = path.Substring(path.LastIndexOf('\\') + 1);

                using (FileStream fs = new FileStream(path, FileMode.Create))
                {
                    BmpBitmapEncoder encoder = new BmpBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(PancamImage.PanCamBitmap));
                    encoder.Save(fs);
                }
            }
        }

        /// <summary>
        /// Checks wether the histogram can be generated
        /// </summary>
        /// <returns></returns>
        private bool GenerateHistogramCanExecute()
        {
            if(PancamImage != null){
                return true;
            }
            return false;
        }

        /// <summary>
        /// Generates the histogram and displays it
        /// </summary>
        private void GenerateHistogramExecute()
        {
            BitmapSource bitmap = PancamImage.PanCamBitmap;

            if(bitmap != null){
                Histogram = imageProcessor.GenerateHistogram(bitmap);
            }
        }

        /// <summary>
        /// Checks wether an image transformation is possible
        /// </summary>
        /// <returns></returns>
        private bool ImageTransformCanExecute(object parameter)
        {
            if (PancamImage != null)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Executes the image transform
        /// </summary>
        private void ImageTransformExecute(object parameter)
        {
            string transformParam = parameter as string;
            switch(transformParam){
                case "desaturate":
                    PancamImage.PanCamBitmap = imageProcessor.GenerateBlackAndWhiteImage(PancamImage.PanCamBitmap);
                    break;
                case "tempUp":
                    PancamImage.PanCamBitmap = imageProcessor.IncreaseColorTemperature(PancamImage.PanCamBitmap);
                    break;
                case "saturate":
                    PancamImage.PanCamBitmap = imageProcessor.Saturate(PancamImage.PanCamBitmap);
                    break;
                default:
                    MessageBox.Show("Command not available");
                    break;
            }
            RaisePropertyChanged("PancamImage");
        }

        /// <summary>
        /// Chesk wether the histogram overlay can be generated
        /// </summary>
        /// <returns>Boolean wether overlay can be executed</returns>
        private bool OverlayCanExecute()
        {
            if(PancamImage.PanCamBitmap != null){
                return true;
            }
            return false;
        }

        /// <summary>
        /// Generates the histogram and siplays it as a new window overlay that always stays on top
        /// </summary>
        private void OverlayExecute()
        {
            HistogramWindow histWindow = new HistogramWindow();
            Image hist = new Image();
            hist.Source = imageProcessor.GenerateHistogram(PancamImage.PanCamBitmap).Source;
            histWindow.SetHistogram(hist);
            histWindow.SetOverlayName(PancamImage.Name);
            histWindow.Show();
        }
        #endregion // Command methods
    }
}
